/*******************************************************************************
 * Copyright (c) 2008 Sylvain Sicard.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Sylvain Sicard - initial API and implementation
 ******************************************************************************/
package fr.jade.console.core;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.prefs.Preferences;

import jline.ArgumentCompletor;
import jline.Completor;
import jline.ConsoleReader;
import jline.History;
import jline.SimpleCompletor;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.NoSuchInterfaceException;

import fr.jade.base.JadeBaseObject;
import fr.jade.console.cmd.Boot;
import fr.jade.console.cmd.Call;
import fr.jade.console.cmd.Cd;
import fr.jade.console.cmd.Connect;
import fr.jade.console.cmd.Desc;
import fr.jade.console.cmd.Disconnect;
import fr.jade.console.cmd.Exit;
import fr.jade.console.cmd.Follow;
import fr.jade.console.cmd.GetEnvVar;
import fr.jade.console.cmd.Lc;
import fr.jade.console.cmd.Ls;
import fr.jade.console.cmd.PrintEnv;
import fr.jade.console.cmd.PrintEnvVar;
import fr.jade.console.cmd.PrintLevel;
import fr.jade.console.cmd.Pwd;
import fr.jade.console.cmd.SelfInit;
import fr.jade.console.cmd.SetAttribute;
import fr.jade.console.cmd.SetEnvVar;
import fr.jade.console.cmd.Source;
import fr.jade.console.cmd.SwitchLevel;
import fr.jade.fraclite.api.control.NoSuchAttributeException;
import fr.jade.util.FractalNavigator;
import fr.jade.util.FractalUtil;
import fr.jade.util.JadeException;

public class JTerm extends JadeBaseObject{

  protected Map<String, Command> commands = null;
	protected Environment env = null;
	protected ConsoleReader reader = null;
	
	public JTerm(){
	  this.commands = new HashMap<String, Command>();
		this.env = new Environment();
		//env.setEnvVar( "current", new Object() );
		env.setEnvVar( "shell", this );
		registerCommands();
	}
	
	public JTerm( String[] args ){
		// not yet implemented
	}
	
	protected void registerCommands(){
	 new Boot().register(commands);
	 new Call().register(commands);
	 new Cd().register(commands);
	 new Connect().register(commands);
	 new Exit().register(commands);
	 new GetEnvVar().register(commands);
	 new Lc().register(commands);
	 new Ls().register(commands);
	 new PrintEnv().register(commands);
	 new PrintLevel().register(commands);
	 new Pwd().register(commands);
	 new SetAttribute().register(commands);
	 new SetEnvVar().register(commands);
	 new Source().register(commands);
	 new SwitchLevel().register(commands);
	 new Help().register(commands);
	 new Desc().register(commands);
	 new Follow().register(commands);
	 new SelfInit().register(commands);
	 new Disconnect().register(commands);
	 new PrintEnvVar().register(commands);
	}
	
	protected Map<String, Command> getConsoleCommands(){
	  return commands;
	}
	public void start(String[] args){
	  for(String cmdLine : args){
	    try{
	      if(!cmdLine.isEmpty()){
	        evaluateCmd( cmdLine );
	      }
      }catch(Throwable e){
        e.printStackTrace();
      }
	  }
		try{
//			BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
//			while(true){
//				log( " >" );
//				String cmdLine = in.readLine();
//				if ( !cmdLine.equals( "" ) ){
//					try{
//						evaluateCmd( cmdLine );
//					}catch(Exception e){
//						e.printStackTrace();
//					}
//				}
//			}
		  
		  reader = new ConsoleReader();
	    List<Completor> completors = new LinkedList<Completor>();
	    completors.add(new SimpleCompletor(commands.keySet().toArray(new String[0])));
	    completors.add(new FractalCompletor(env));
	    reader.addCompletor(new ArgumentCompletor(completors));
	    reader.setHistory(loadHistory());
	    String cmdLine;
	    while (true) {
	      cmdLine = reader.readLine(getPrompt());
        if ( !cmdLine.equals( "" ) ){
          evaluateCmd( cmdLine );
        }	      
	    }
		}catch(Exception e){
			log("Fatal error in JTerm");
			e.printStackTrace();
		}
	}
	
	public void evaluateCmd(String cmdLine){
		String[] cmd = cmdLine.split(" ");
		try{
			Command c = commands.get(cmd[0]);
			if(c == null) {
			  System.err.println("JTerm: "+cmd[0]+" command not found");
			}else{
			  c.execute(cmd, env);
			}
		}catch(DisconnectedException e){
		  System.out.println("Terminal is not connected to a MiniJade instance");
		}catch(Throwable e){
			System.out.println("JTerm # Error");
			e.printStackTrace();
		}
	}
	
	private String getPrompt(){
	  String prompt = "";
	  Component current = (Component)env.getEnvVar("current");
	  if(current != null){
  	  if(current != null){
  	    String host = "?";
  	    try {
  	      Component node = null;
  	      try{
  	        node = FractalNavigator.getNode(current);
  	      } catch (AssertionError e){
            node = current; 
          }
  	      if(node != null)
  	        host = FractalUtil.getAttributeController(node).getAttribute("hostName");
        } catch (JadeException e) {
        } catch (NoSuchAttributeException e) {
        } catch (NoSuchInterfaceException e) { 
        } 
  	    String name = FractalNavigator.getName(current);
  	    prompt = ColorPrinter.getColoredMessage(host, ColorPrinter.STATUS)
  	            +":"
  	            +ColorPrinter.getColoredMessage(name, ColorPrinter.PROMPT);
  	  }
	  }
	  return prompt+"> ";
	}

	
	private History loadHistory() {
    jline.History jHistory = new jline.History();
    try {
      Preferences prefs = Preferences.userRoot()
          .node(this.getClass().getName());
      String[] historyKeys = prefs.keys();
      Arrays.sort(historyKeys, 0, historyKeys.length);
      for (int i = 0; i < historyKeys.length; i++) {
        String key = historyKeys[i];
        String value = prefs.get(key, "");
        jHistory.addToHistory(value);
      }
    } catch (Exception e) {
      // unable to load prefs: do nothing
    }
    return jHistory;
  }

  /**
   * Retrieve the command history
   * 
   * @return a List including the command history
   */
  public List getHistory() {
    return reader.getHistory().getHistoryList();
  }

  /**
   * Store the current command history
   */
  public void storeHistory() {
    List history = reader.getHistory().getHistoryList();
    try {
      Preferences prefs = Preferences.userRoot()
          .node(this.getClass().getName());
      prefs.clear();
      int historySize = history.size();
      for (int i = 0; i < historySize; i++) {
        prefs.put(String.valueOf(i ), (String) history.get(i));
      }
      prefs.flush();
    } catch (Exception e) {
      // unable to store prefs: do nothing
    }
  }
}
